package com.derekandbritt.booST.server;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.derekandbritt.booST.data.Challenge;
import com.derekandbritt.booST.data.EmotionLevel;
import com.derekandbritt.booST.data.EnergyLevel;
import com.derekandbritt.booST.data.User;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;

public class JSONEndpoint extends HttpServlet {

	static final long serialVersionUID = 1L;

	private static String METHOD_TEST_SERVICE = "/JSON/ServiceTest";
	
	private static String METHOD_JSON_GET_USER = "/JSON/User";
	private static String METHOD_JSON_SET_USER = "/JSON/User";
	
	private static String METHOD_JSON_GET_ENERGY_LEVEL = "/JSON/EnergyLevel";
	private static String METHOD_JSON_SET_ENERGY_LEVEL = "/JSON/EnergyLevel";
	
	private static String METHOD_JSON_GET_CHALLENGES = "/JSON/Challenges";
	private static String METHOD_JSON_SET_CHALLENGES = "/JSON/Challenges";
	
	private static String METHOD_JSON_GET_EMOTION_LEVEL = "/JSON/EmotionLevel";
	private static String METHOD_JSON_SET_EMOTION_LEVEL = "/JSON/EmotionLevel";
	private static String METHOD_JSON_SET_EMOTION_LEVEL_TEMP = "/JSON/EmotionLevelTemp";
	
	private static final Logger logger = Logger.getLogger("JSONEndpoint");
	
	private Gson gson = new Gson();
	private DataManager dataManager = new DataManager();
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
	    String requestor = request.getParameter("requestor");
        dataManager.updateLastActivity(requestor);
	    
	    if(METHOD_TEST_SERVICE.equals(request.getRequestURI())) {
			handleTestService(request, response);
		} else if(METHOD_JSON_GET_USER.equals(request.getRequestURI())) {
			handleGetUser(request, response);
		} else if(METHOD_JSON_GET_ENERGY_LEVEL.equals(request.getRequestURI())) {
			handleGetEnergyLevel(request, response);
		} else if(METHOD_JSON_GET_CHALLENGES.equals(request.getRequestURI())) {
			handleGetChallenges(request, response);
		} else if(METHOD_JSON_GET_EMOTION_LEVEL.equals(request.getRequestURI())) {
			handleGetEmotionLevel(request, response);
		} else if(METHOD_JSON_SET_EMOTION_LEVEL_TEMP.equals(request.getRequestURI())) {
		    handleSetEmotionLevelTemp(request, response);
        }
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    
	    String requestor = request.getParameter("requestor");
        dataManager.updateLastActivity(requestor);
	    
        
        if(METHOD_JSON_SET_USER.equals(request.getRequestURI())) {
            handleSetUser(request, response);
        } else if(METHOD_JSON_SET_ENERGY_LEVEL.equals(request.getRequestURI())) {
			handleSetEnergyLevel(request, response);
		} else if(METHOD_JSON_SET_CHALLENGES.equals(request.getRequestURI())) {
			handleSetChallenge(request, response);
		} else if(METHOD_JSON_SET_EMOTION_LEVEL.equals(request.getRequestURI())) {
			handleSetEmotionLevel(request, response);
		}
	}
	
	private void handleGetUser(HttpServletRequest request, HttpServletResponse response) {
		
		String userId = request.getParameter("userId");
		String flagString = request.getParameter("flags");
		
		int flags = 0;
		try { flags = Integer.parseInt(flagString); }
		catch(NumberFormatException e) { /* acceptable behavior */}
		
		try {
			User result = dataManager.getUser(userId, flags);
			String json = gson.toJson(result, User.class);
			
			response.setContentType("application/json");
			
			ObjectOutputStream oos = new ObjectOutputStream(response.getOutputStream());
			oos.reset();
			oos.writeObject(json);
			oos.flush();
			oos.close();
			
		} catch (IOException e) {
		    logger.log(Level.SEVERE, "Unable to return the user object", e);
		}
	}
	
	private void handleSetUser(HttpServletRequest request, HttpServletResponse response) {
	    String userId = request.getParameter("userId");
        String onlineString = request.getParameter("online");
	        
        boolean online = Boolean.parseBoolean(onlineString);
	        
        // Send the data to booST
        if(dataManager.updateUser(userId, online))
            response.setStatus(HttpServletResponse.SC_OK);
        else
            response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
	}
	
	private void handleTestService(HttpServletRequest request, HttpServletResponse response) {
		
		try {
			boolean result = dataManager.isServiceOperational();
			String json = gson.toJson(result);
			
			response.setContentType("application/json");
			
			ObjectOutputStream oos = new ObjectOutputStream(response.getOutputStream());
			oos.reset();
			oos.writeObject(json);
			oos.flush();
			oos.close();
			
		} catch (IOException e) {
		    logger.log(Level.SEVERE, "Unable to return the server's status", e);
		}
	}
	
	private void handleGetEnergyLevel(HttpServletRequest request, HttpServletResponse response) {
		
		String userId = request.getParameter("userId");
		String currentEmotion = request.getParameter("current");
		String resultCount = request.getParameter("count");
		
		int count = 1;
		try { count = Integer.parseInt(resultCount); }
		catch(NumberFormatException e) { /* acceptable behavior */}
		
		boolean current = Boolean.parseBoolean(currentEmotion);
		
		try {
		    String json;
		    if(!current) {
		        List<EnergyLevel> result = dataManager.getPastEnergyLevels(userId, count);
		        Type listType = new TypeToken<List<EnergyLevel>>() {}.getType();
		        json = gson.toJson(result, listType);
		    } else {
		        List<EnergyLevel> result = new ArrayList<EnergyLevel>();
	            result.add(dataManager.getCurrentEnergyLevel(userId));
                Type listType = new TypeToken<List<EnergyLevel>>() {}.getType();
                json = gson.toJson(result, listType);
		    }
			
			response.setContentType("application/json");
			
			ObjectOutputStream oos = new ObjectOutputStream(response.getOutputStream());
			oos.reset();
			oos.writeObject(json);
			oos.flush();
			oos.close();
			
		} catch (IOException e) {
		    logger.log(Level.SEVERE, "Unable to return the user's energy level", e);
		}
	}
	
	private void handleSetEnergyLevel(HttpServletRequest request, HttpServletResponse response) throws IOException {

	    String jsonString = request.getParameter("content");
		
		try {
		
			// Ensure that the provided sensor data is in the correct format
			EnergyLevel energyLevel = new Gson().fromJson(jsonString, EnergyLevel.class);
			
			// Send the data to booST
			dataManager.setEnergyLevel(energyLevel);
			
			response.setStatus(HttpServletResponse.SC_OK);
			
		} catch (JsonParseException e) {
		    logger.log(Level.SEVERE, "Unable to parse the provided energy level", e);
		}		
	}
	
	private void handleGetEmotionLevel(HttpServletRequest request, HttpServletResponse response) {
		
		String userId = request.getParameter("userId");
		
		try {
			EmotionLevel result = dataManager.getEmotionLevel(userId);
			String json = gson.toJson(result, EmotionLevel.class);
			
			response.setContentType("application/json");
			
			ObjectOutputStream oos = new ObjectOutputStream(response.getOutputStream());
			oos.reset();
			oos.writeObject(json);
			oos.flush();
			oos.close();
			
		} catch (IOException e) {
		    logger.log(Level.SEVERE, "Unable to return the user's emotion level", e);
		}
	}

	private void handleSetEmotionLevelTemp(HttpServletRequest request, HttpServletResponse response) throws IOException {

        String userId = request.getParameter("userId");
        String levelString = request.getParameter("level");
        
        double level = 0;
        try { level = Double.parseDouble(levelString); }
        catch(NumberFormatException e) { /* acceptable behavior */}
        
        
        EmotionLevel emotionLevel = new EmotionLevel(userId, level);
        
        // Send the data to booST
        dataManager.setEmotionLevel(emotionLevel);
            
        response.setStatus(HttpServletResponse.SC_OK);
    }
	
	private void handleSetEmotionLevel(HttpServletRequest request, HttpServletResponse response) throws IOException {

	    String jsonString = request.getParameter("content");
		
		try {
		
			// Ensure that the provided sensor data is in the correct format
			EmotionLevel emotionLevel = new Gson().fromJson(jsonString, EmotionLevel.class);
			
			// Send the data to booST
			dataManager.setEmotionLevel(emotionLevel);
			
			response.setStatus(HttpServletResponse.SC_OK);
			
		} catch (JsonParseException e) {
		    logger.log(Level.SEVERE, "Unable to parse the provided emotion level", e);
		}		
	}

	
	private void handleGetChallenges(HttpServletRequest request, HttpServletResponse response) {
		
		String challengeId = request.getParameter("id");
		String userId = request.getParameter("userId");
		String status = request.getParameter("status");
		String resultCount = request.getParameter("count");
		
		int count = 10;
		try { count = Integer.parseInt(resultCount); }
		catch(NumberFormatException e) { /* acceptable behavior */}
		
		int id = -1;
		try { id = Integer.parseInt(challengeId); }
		catch(NumberFormatException e) { /* acceptable behavior */}
		
		try {
			
			String json;
			
			if(id > 0) {
				Challenge result = dataManager.getChallenge(id);
				json = gson.toJson(result);
			} else {
				
				List<Challenge> result;
				
				if(status != null && status.equals("active"))
					result = dataManager.getActiveChallenges(userId);
				else if(status != null && status.equals("completed"))
				    result = dataManager.getCompletedChallenges(userId, count);
				else
					result = dataManager.getChallenges(userId, count);
				
				Type listType = new TypeToken<List<Challenge>>() {}.getType();
				json = gson.toJson(result, listType);
			}
			
			response.setContentType("application/json");
			
			ObjectOutputStream oos = new ObjectOutputStream(response.getOutputStream());
			oos.reset();
			oos.writeObject(json);
			oos.flush();
			oos.close();
			
		} catch (IOException e) {
		    logger.log(Level.SEVERE, "Unable to return the user's challenges", e);
		}
	}
	
	private void handleSetChallenge(HttpServletRequest request, HttpServletResponse response) throws IOException {

	    String jsonString = request.getParameter("content");
	    
		try {
		
			Challenge challenge = new Gson().fromJson(jsonString, Challenge.class);
			
			// Send the data to booST
			dataManager.setChallenge(challenge);
			
			response.setStatus(HttpServletResponse.SC_OK);
			
		} catch (JsonParseException e) {
		    logger.log(Level.SEVERE, "Unable to parse the provided challenge", e);
		}		
	}
}
